package org.oos.dao;

import static org.oos.dao.options.OptionsMap.FINDERS.FIELD_OPTIONS;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.oos.atom.AtomEntity;
import org.oos.dao.options.FieldOptions;

import com.google.api.client.util.Key;

/**
 * 
 * This static class is in charge of filling the {@link FieldOptions} from the
 * provided {@link AbstractDao}. It keeps a cache of the field names to avoid
 * using Reflection more than needed.
 * 
 * @author pablo.gonzalez@11870.com
 * 
 */
public class FieldPacker {

	private static Map<Class<? extends AtomEntity>, List<String>> fieldCache = new HashMap<Class<? extends AtomEntity>, List<String>>();

	public static FieldOptions fill(Dao<?, ?> dao) {
		if (!(dao instanceof AbstractDao<?, ?>)) {
			throw new IllegalStateException(
					"This can only be used with an AbstractDao implementation");
		}
		Class<? extends AtomEntity> entryClass = ((AbstractDao<?, ?>) dao)
				.getEntryType();
		List<String> fieldNames;
		FieldOptions fieldOptions = FIELD_OPTIONS.get(dao.getOptions());
		if (fieldCache.containsKey(entryClass)) {
			fieldNames = fieldCache.get(entryClass);
			for (String name : fieldNames) {
				fieldOptions.add(name);
			}
		} else {
			fieldNames = new ArrayList<String>();
			processRecursiveFields(entryClass, fieldNames, fieldOptions);
		}

		return fieldOptions;
	}

	private static List<Field> processRecursiveFields(Class<?> klass,
			List<String> fieldNames, FieldOptions fieldOptions) {
		List<Field> fields = new ArrayList<Field>();
		if (klass.getSuperclass() != Object.class) {
			fields.addAll(processRecursiveFields(klass.getSuperclass(),
					fieldNames, fieldOptions));
		}
		for (Field field : klass.getDeclaredFields()) {
			Key key = field.getAnnotation(Key.class);
			if (key != null) {
				String name = null;
				if (!key.value().equals("##default")) {
					name = key.value();
				} else {
					name = field.getName();
				}
				fieldOptions.add(name);
				fieldNames.add(name);
			}
		}
		return fields;
	}

}
